package dlally.model;

import java.util.ArrayList;

/**
 * The model, responsible for storing and adding information about a DecisionLines Event
 * @author Douglas Lally (dlally@wpi.edu)
 *
 */
public class DecisionLinesEvent {
	
	int numRounds;
	Line[] lines;
	/**
	 * @param numRounds the number of rounds to be played
	 * @param numLines the number of lines/players that are participating
	 */
	public DecisionLinesEvent(int numRounds, int numLines) {
		super();
		this.numRounds = numRounds;
		this.lines = new Line[numLines];
		for(int i = 0; i < numLines; i++){
			lines[i] = new Line(i);
		}
	}
	/**
	 * Verifies that the given edge can be added to this DecisionLinesEvent
	 * @param e the edge to check
	 * @return true if it can be added, false otherwise
	 */
	boolean isValidEdge(Edge e){
		// First make sure the line is between two valid edges
		int left = e.getLeft();
		int right = e.getRight();
		if(left < 0)
			return false;
		if(right > lines.length - 1)
			return false;
		
		int height = e.getHeight();
		Line leftLine = lines[e.getLeft()];
		Line rightLine = lines[e.getRight()];
		ArrayList<Edge> leftLineEdges = leftLine.getEdges();
		ArrayList<Edge> rightLineEdges = rightLine.getEdges();
		// If an edge is valid, it must not be within 7 pixels of another edge in the same colum
		// or to the left or right
		
		// First, check it against the height of all edges in the left line
		for(int i = 0; i < leftLineEdges.size(); i++){
			if(Math.abs(height - leftLineEdges.get(i).getHeight()) < 7)
				return false;
		}
		
		// Now check it against the height of all edges in the right line
		for(int j = 0; j < rightLineEdges.size(); j++){
			if(Math.abs(height - rightLineEdges.get(j).getHeight()) < 7)
				return false;
		}
		// If it gets this far, then the edge must be valid
		return true;
	}
	/**
	 * Verifies that an edge is valid. Prints an error message if invalid. If it is valid, then it adds the edge to the appropriate lines
	 * @param e The edge to add
	 * @return true if the edge was added, false otherwise
	 */
	public boolean addEdge(Edge e){
		if(e == null){
			System.out.println("Tried to add null edge");
			return false;
		}
		if(!this.isValidEdge(e)){
			System.out.println("Failed to add edge. Left: " + e.getLeft() + ". Right: " + e.getRight() + ". Height: " + e.getHeight() + ".");
			return false;
		}
		else{
			Line leftLine = lines[e.getLeft()];
			Line rightLine = lines[e.getRight()];
			leftLine.addEdgeInOrder(e);
			rightLine.addEdgeInOrder(e);
			//System.out.println("Added edge. Left: " + e.getLeft() + ". Right: " + e.getRight() + ". Height: " + e.getHeight() + ".");
			return true;
		}
	}
	/**
	 * Checks if a string could be a valid choice
	 * @param value the string to check for validity
	 * @return true if the choice is valid, false otherwise
	 */
	boolean isChoiceValid(int position, String value){
		// A choice is invalid if it is blank, or a duplicate of an existing one
		if(value == null){
			System.out.println("Error: Tried to set choice to null");
			return false;
		}
		if(value.equals("")){
			System.out.println("Error: Tried to set choice to blank");
			return false;
		}
		if(position < 0 || position > lines.length - 1)
			return false;
		for(int i = 0; i < lines.length; i++){
			if(lines[i].getChoice().toLowerCase().equals(value.toLowerCase())){
				System.out.println("Error: Choice already exists");
				return false;
			}
		}
		return true;
	}
	
	/**
	 * Verifies that a choice name is valid. If it is, it sets the line of the given index to have the given choice
	 * @param position the index of the line to set
	 * @param choice the choice to set
	 * @return true if it was able to set choice, false otherwise
	 */
	public boolean setChoice(int position, String choice){
		if(!this.isChoiceValid(position, choice))
			return false;
		else{
			lines[position].setChoice(choice);
			return true;
		}
	}
	
	/**
	 * Checks if this event is done, by making sure there are numLines * numRounds edges
	 * @return true if done, false if still in progress
	 */
	public boolean isDone(){
		//System.out.println("Looking for a total of " + (numRounds * lines.length) + " edges");
		int totalEdges = 0;
		// Count the edges for every other line, since adjacent lines will have some overlap
		for(int i = 0; i < lines.length; i+=2){
		//System.out.println("Examining line " + i +"\nIt has " + lines[i].numEdges() + " edges");
			
			totalEdges += lines[i].numEdges();
		}
		//System.out.println("Found " + totalEdges + " edges");
		if(totalEdges == (numRounds * lines.length))
			return true;
		else
			return false;
		
	}
	/**
	 * Computes a Decision Order, based on the edges that were added and the position of the original choices
	 * @return An array of strings in the appropriate order
	 */
	public String[] prioritizedChoices(){
		String[] answer = new String[lines.length];
		int finalPosition;
		int height;
		Edge next;
		for(int i = 0; i < lines.length; i++){
			//System.out.println("Examining choice " + lines[i].getChoice());
			finalPosition = i;
			height = -1;
			next = lines[i].nextEdge(height);
			//System.out.println("==Starting from line " + i +"==");
			while(next != null){
				finalPosition = swapLine(next, finalPosition);
				//System.out.println("New position: " + finalPosition + "\n");
				height = next.getHeight();
				next = lines[finalPosition].nextEdge(height);
			}
			//System.out.println("Putting it into " + finalPosition);
			answer[finalPosition] = lines[i].getChoice();
				
		}
		return answer;
	}
	/**
	 * Helper method to swap from one side of an edge to the other
	 * @param e the edge
	 * @param current the current line
	 * @return the index of the other line that the edge is connected to
	 */
	int swapLine(Edge e, int current){
		//System.out.println("Left of this edge: " + e.getLeft() + "\nRight of this edge: " + e.getRight() + "\nCurrent line: " + current);
		if(e.getLeft() == current)
			return e.getRight();
		return e.getLeft();
	}

	/**
	 * @return the numRounds
	 */
	public int getNumRounds() {
		return numRounds;
	}
	
	/**
	 * @return the numPlayers
	 */
	public int getNumPlayers(){
		return lines.length;
	}
	/**
	 * 
	 * @return the lines for this event
	 */
	public Line[] getLines(){
		return lines;
	}
}
